
#include "MeshReader.h"
#include "Entity.h"
#include "SubdivisionGeom.h"
#include "3ds.h"
#include "TEString.h"
#include <fstream>

//
// Read 3DS (3D Studio Max) Files
//

bool Read3DSMesh(Entity *parent, const char *fileName)
{
	// Create the name
	char _fileName[256];
	sprintf_s(_fileName, 256, "..\\data\\%s.3ds", fileName);

	// Loading 3DS using file 3ds.h
	C3DS obj;
	if(!obj.Create(_fileName))
		return false;

	// For each mesh
	for(int i = 0; i < obj.m_iNumMeshs; ++i)
	{
		// Create a new Entity under the parent
		Entity *entity = new Entity(parent);
		SubdivisionGeom *geom = entity->CreateSubGeom();

		// For each vertex
		for(int j = 0; j < obj.m_pMeshs[i].iNumVerts; ++j)
		{
			geom->AddVertex(Real3(obj.m_pMeshs[i].pVerts[j].x,
											obj.m_pMeshs[i].pVerts[j].y,
											obj.m_pMeshs[i].pVerts[j].z));
		}

		// For each triangle
		for(int j = 0; j < obj.m_pMeshs[i].iNumFaces; ++j)
		{
			geom->AddTriangle(obj.m_pMeshs[i].pFaces[j].A,
											  obj.m_pMeshs[i].pFaces[j].B,
											  obj.m_pMeshs[i].pFaces[j].C);
		}

		// Material
		entity->SetMaterial();
		//obj.m_pMaterials[0].
		//obj.m_pMeshs[i].pFaces[0].MaterialID

		// Since none are provided, we will have to make our own
		geom->CalculateVertexNormals();
	}

	// Release the data
	obj.Release();

	// Success
	return true;
}

//
// Read X (DirectX) Files
//

uint CreateFrame(Entity *parent, const vector<string> &toks, uint i);
uint CreateMesh(SubdivisionGeom *geom, const vector<string> &toks, uint i);
uint CreateMaterial(Geometry *geom, const vector<string> &toks, uint i);
uint SkipSection(const vector<string> &toks, uint i);

#define REVERSE_FACES

bool ReadXMesh(Entity *parent, const char *fileName)
{
	// Create the name
	char _fileName[256];
	sprintf_s(_fileName, 256, "..\\data\\%s.x", fileName);

	// Create file
	ifstream file(_fileName);
  if(file.is_open())
	{
		string line;
		vector<string> toks;

		// Tokenize
		while(!file.eof())
		{
			// Get line
			getline(file, line);

			// Current i
			uint i = toks.size();

			// Tokenize
			Tokenize(line, toks, " ;,");

			// Search for comments (//)
			while(i < toks.size())
			{
				if(toks[i].size() > 1 && toks[i][0] == '/' && toks[i][1] == '/')
					toks.resize(i);
				++i;
			}
		}

		file.close();

		// Go through each token and create necessary data
		uint i = 1;

		while(i < toks.size())
		{
			if(toks[i] == "template")
				i = SkipSection(toks, i+3);
			else if(toks[i] == "Frame")
				i = CreateFrame(parent, toks, i+1);
			else if(toks[i] == "Mesh")
			{
				Entity *e = new Entity(parent);
				i = CreateMesh(e->CreateSubGeom(), toks, i+3);
				//e->SetMaterial();
			}
			else if(toks[i] == "{")
				i = SkipSection(toks, i+1);
			else ++i;
		}
	}
	else return false;

	// Success
	return true;
}

uint CreateFrame(Entity *parent, const vector<string> &toks, uint i)
{
	string name = toks[i++];
	Entity *e = new Entity(parent, name);
	//e->SetMaterial();
	++i;

	while(i < toks.size() && !(toks[i] == "}"))
	{
		if(toks[i] == "FrameTransformMatrix")
		{
			i = SkipSection(toks, i+2);
		}
		else if(toks[i] == "Frame")
		{
			// Frame Name = toks[i+1]
			i = CreateFrame(e, toks, i+1);
		}
		else if(toks[i] == "Mesh")
		{
			i = CreateMesh(e->CreateSubGeom(), toks, i+2);
		}
		else if(toks[i] == "{")
			i = SkipSection(toks, i+1);
		else
			++i;
	}

	return i+1;
}

uint CreateMesh(SubdivisionGeom *geom, const vector<string> &toks, uint i)
{
	// Number of vertices
	uint vSize = GetInt(toks[i++]);

	// Vertex data
	vector<Real3> pos(vSize);
	vector<Real3> norm(vSize);
	vector<Real2> tex(vSize);
	uint index = 0;

	while(index < vSize)
	{
		float x = GetFloat(toks[i++]);
		float y = GetFloat(toks[i++]);
		float z = GetFloat(toks[i++]);
		//i++;

		pos[index++].Set(x, y, z);
	}
	
	// Number of faces
	uint fSize = GetInt(toks[i++]);
	index = 0;

	while(index++ < fSize)
	{
		ushort sides = GetInt(toks[i++]);

		ushort a = GetInt(toks[i++]);
		ushort b = GetInt(toks[i++]);
		ushort c = GetInt(toks[i++]);

		if(sides == 3)
		{
			#ifdef REVERSE_FACES
			geom->AddTriangle(c, b, a);
			#else
			geom->AddTriangle(a, b, c);
			#endif
		}
		else if(sides == 4)
		{
			ushort d = GetInt(toks[i++]);
			#ifdef REVERSE_FACES
			geom->AddQuad(c, b, a, d);
			#else
			geom->AddQuad(a, b, c, d);
			#endif
		}
		else
		{
			// Error
		}
	}

	// Skip material stuff
	i = CreateMaterial(geom, toks, i+2);

	// Calculate Normals
	bool calNormals = true;

	// Mesh Texture Coordinates
	if(toks[i] == "MeshTextureCoords")
	{
		i += 2;
		index = 0;

		if(GetInt(toks[i++]) == vSize)
		{
			while(index < vSize)
			{
				float u = GetFloat(toks[i++]);
				float v = GetFloat(toks[i++]);

				tex[index].Set(u, v);
				++index;
			}
		}

		while(!(toks[i] == "}"))
			++i;

		++i;
	}

	// Mesh Normals
	if(toks[i] == "MeshNormals")
	{
		i += 2;
		index = 0;

		if(GetInt(toks[i++]) == vSize)
		{
			while(index < vSize)
			{
				float x = GetFloat(toks[i++]);
				float y = GetFloat(toks[i++]);
				float z = GetFloat(toks[i++]);

				norm[index].Set(x, y, z);
				++index;
			}

			calNormals = false;
		}

		while(!(toks[i] == "}"))
			++i;
	}
	
	// Create Vertices
	for(uint j = 0; j < vSize; j++)
		geom->AddVertex(pos[j], norm[j], tex[j]);

	// Merge Duplicate Vertices
	geom->MergeDuplicateVertices();

	// Calculate Normals
	if(calNormals)
		geom->CalculateVertexNormals();

	return i+2;
}

uint CreateMaterial(Geometry *geom, const vector<string> &toks, uint i)
{
	while(!(toks[i] == "}"))
	{
		if(toks[i] == "Material")
		{
			while(!(toks[i] == "{")) ++i; ++i;

			Color faceColor(GetFloat(toks[i++]), GetFloat(toks[i++]), GetFloat(toks[i++]), GetFloat(toks[i++]));
			//Color faceColor(GetFloat(toks[i++]), GetFloat(toks[i++]), GetFloat(toks[i++])); GetFloat(toks[i++]);
			float power = GetFloat(toks[i++]);
			Color specularColor(GetFloat(toks[i++]), GetFloat(toks[i++]), GetFloat(toks[i++]));
			Color emissiveColor(GetFloat(toks[i++]), GetFloat(toks[i++]), GetFloat(toks[i++]));

			// Set parent's material
			geom->Parent()->SetMaterial(faceColor, __black, specularColor, emissiveColor, power);

			if(toks[i] == "TextureFilename")
			{
				i+=2;
				// Get File Name
				string fileName = "..\\images\\textures\\" + toks[i].substr(1, toks[i].size()-2);
				tManager->CreateTexture(ConvertToTChar(fileName));

				++i;
			}
			++i;
		}
		++i;
	}

	return i+1;
}

uint SkipSection(const vector<string> &toks, uint i)
{
	int count = 1;

	while(i < toks.size() && count != 0)
	{
		if(toks[i] == "{")
			++count;
		else if(toks[i] == "}")
			--count;

		++i;
	}

	return i;
}